(function () {
	var ns = ij.pkg('core').namespaceString;
	ij.core.DisplayObject = ij.core.Observable.extend({
		init: function (config) {
			return this._super(ij.apply({
				classname: ns + '.DisplayObject',
				x: 0,
				y: 0,
				z: 1,
				angle: 0,
				width: 0,
				height: 0,
				dom: null,
				parent: null,
				classes: ['ij-object'],
				attrs: {},
				visible: true,
				sprite: {
					color: 'transparent',
					url: null,
					x: 0,
					y: 0,
					repeat: 'no-repeat'
				},
				transformOrigin: {
					dx: 0,
					dy: 0,
					dz: 0
				},
				renderer: {
					classname: 'ij.core.DomRenderer'
				}
			}, config));
		},
		apply: function (extension, overwrite_existing) {
			var config = ij.clone(extension);
			config.x = this.x || 0;
			config.y = this.y || 0;
			config.z = this.z || 0;
			config.angle = this.angle || 0;
			config.width = this.width || 0;
			config.height = this.height || 0;
			config.background = ij.apply(ij.apply({},this.background), config.background);
			
			this._super(config, overwrite_existing);
			
			if (extension.x || extension.y || extension.z) {
				this.moveTo(extension.x, extension.y, extension.z);
			}
			if (extension.width || extension.height) {
				this.setSize(extension.width, extension.height);
			}
			if (extension.angle) {
				this.rotate(extension.angle);
			}
			if (extension.transformOrigin) {
				this.setTransformOrigin(extension.transformOrigin);
			}
				
			this.setRenderer();
			this.setSprite();
			
			return this;
		},
		isRendered: function () {
			return this.renderer && this.renderer.isRendered();
		},
		show: function () {
			if (this.isRendered() && !this.isVisible()) {
				this.renderer.show();
				
				this.dispatchEvent(new ij.core.Event({
					name: 'show'
				}));
			}
			return this;
		},
		hide: function () {
			if (this.isRendered() && this.isVisible()) {
				this.renderer.hide();
				
				this.dispatchEvent(new ij.core.Event({
					name: 'hide'
				}));
			}
			return this;
		},
		isVisible: function () {
			return this.renderer && this.renderer.isVisible();
		},
		setSize: function (width, height) {
			width = width || this.width;
			height = height || this.height;
			if (!(width == this.width && height == this.height)) {
				this.width = width;
				this.height = height;
				
				this.dispatchEvent('resize');
			}
			return this;
		},
		setTransformOrigin: function (transformOrigin) {
			transformOrigin = transformOrigin || {};
			if (this.transformOrigin.dx != transformOrigin.dx ||
					this.transformOrigin.dy != transformOrigin.dy ||
					this.transformOrigin.dz != transformOrigin.dz) {
				this.transformOrigin.dx = typeof transformOrigin.dx == 'undefined'?this.transformOrigin.dx:transformOrigin.dx;
				this.transformOrigin.dy = typeof transformOrigin.dy == 'undefined'?this.transformOrigin.dy:transformOrigin.dy;
				this.transformOrigin.dz = typeof transformOrigin.dz == 'undefined'?this.transformOrigin.dz:transformOrigin.dz;
				if (this.renderer) {
					this.renderer.setTransformOrigin();
				}
				
				this.dispatchEvent('transformOriginChange');
			}
			return this;
		},
		moveTransformOriginTo: function (dx, dy, dz) {
			return this.setTransformOrigin({
				dx: dx,
				dy: dy,
				dz: dz
			});
		},
		moveTo: function (x, y, z) {
			if (this.x != x || this.y != y || this.z != z) {
				this.x = (typeof x == 'undefined' || x === null)?this.x:x;
				this.y = (typeof y == 'undefined' || y === null)?this.y:y;
				this.z = (typeof z == 'undefined' || z === null)?this.z:z;
				
				this.dispatchEvent('move');
			}
			
			return this;
		},
		moveBy: function (dx, dy, dz) {
			dx = dx || 0;
			dy = dy || 0;
			dz = dz || 0;
			if (dx || dy || dz) {
				this.moveTo(this.x + dx, this.y + dy, this.z + dz);
			}
			
			return this;
		},
		rotate: function (angle) {
			if (this.angle != angle) {
				this.angle = (typeof angle == 'undefined' || angle === null)?this.angle:angle;
				
				this.dispatchEvent('rotate');
			}
			
			return this;
		},
		rotateBy: function (angle) {
			if (this.angle) {
				this.rotate(this.angle + angle);
			}
			return this;
		},
		setSprite: function (sprite) {
			ij.apply(this.sprite, sprite);
			
			return this;
		},
		moveSpriteTo: function (x, y) {
			this.setSprite({
				x: (typeof x == 'undefined'?this.sprite.x:x),
				y: (typeof y == 'undefined'?this.sprite.y:y)
			});
			
			return this;
		},
		moveSpriteBy: function (dx, dy) {
			dx = dx || 0;
			dy = dy || 0;
			if (dx || dy) {
				this.moveSpriteTo(this.sprite.x + dx, this.sprite.y + dy);
			}
			
			return this;
		},
		setRenderer: function (renderer) {
			renderer = renderer || this.renderer;
			if (!(renderer instanceof ij.core.Renderer)) {
				renderer = new (ij.namespace(renderer.classname))({
					displayObject: this
				});
			}
			
			this.renderer = renderer;
			
			return this;
		},
		render: function () {
			if (this.renderer) {
				this.renderer.render();
			}
			
			return this;
		},
		update: function () {
			return this;
		},
		getX: function () {
			return this.x;
		},
		getY: function () {
			return this.y;
		},
		getZ: function () {
			return this.z;
		},
		getAngle: function () {
			return this.angle;
		},
		getSprite: function () {
			return this.sprite;
		},
		getTransformOrigin: function () {
			return this.transformOrigin;
		}
	});
})();
